﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data;
using System.ComponentModel;

namespace PominaScale.Controls
{
    internal class AutoCompleteComboBox : ComboBox
    {

        #region Variables / Events

        private DataView dataViewCombo;
        private string lastErrorDescription;
        private string stackTraceError;
        private int autoCompleteHeight = 120;
        private int defaultHeight = 0;

        public delegate void KeyUpEventHandler(object sender, System.Windows.Forms.KeyEventArgs e);
        public delegate void LeaveEventHandler(object sender, System.EventArgs e);
        public new event KeyUpEventHandler KeyUp;
        public new event LeaveEventHandler Leave;

        // Event activated when enter is typed in the combo-box
        public event KeyEventHandler Submit;

        #endregion

        #region Proprieties

        [CategoryAttribute("AutoCompleteComboBox Property"),
            Browsable(false),
           ReadOnly(true),
           BindableAttribute(true),
           DefaultValueAttribute(""),
           DesignOnly(true),
           DescriptionAttribute("Descrição do último erro ocorrido no componente.")]
        public string LastErrorDescription
        {
            // Last error that occurred in the component
            get
            {
                return lastErrorDescription;
            }
        }

        [CategoryAttribute("AutoCompleteComboBox Property"),
            Browsable(false),
           ReadOnly(true),
           BindableAttribute(true),
           DefaultValueAttribute(""),
           DesignOnly(true),
           DescriptionAttribute("Stack trace do último erro ocorrido no componente.")]
        public string StackTraceError
        {
            // Stack trace of the last error
            get
            {
                return stackTraceError;
            }
        }

        [CategoryAttribute("AutoCompleteComboBox Property"),
          Browsable(true),
          ReadOnly(false),
          BindableAttribute(true),
          DefaultValueAttribute(120),
          DesignOnly(false),
          DescriptionAttribute("Altura do combo quando mostra opções de auto-complete.")]
        public int AutoCompleteHeight
        {
            // Combo height when showing auto-complete options
            get
            {
                return autoCompleteHeight;
            }
            set
            {
                autoCompleteHeight = value;
            }
        }

        #endregion

        #region Constructors
        public AutoCompleteComboBox()
        {
            this.ValueMember = "Value";
            this.DisplayMember = "Text";
            this.DropDownStyle = ComboBoxStyle.Simple;
        }
        #endregion

        #region Methods

        public void AddDataSource(DataTable dataTable, string displayMember, string valueMember)
        {

            // Create a DataView from the DataTable and sets 
            // as Combo DataSource

            try
            {
                DataTable table = dataTable.Copy();

                dataViewCombo = table.DefaultView;
                this.DisplayMember = displayMember;
                this.ValueMember = valueMember;
                this.DataSource = dataViewCombo;

            }
            catch (Exception ex)
            {
                // Verify errors
                lastErrorDescription = ex.Message;
                stackTraceError = ex.StackTrace;
                this.ShowError();
            }
        }

        protected override void OnEnter(EventArgs e)
        {
            base.OnEnter(e);

            if (0 == defaultHeight)
                defaultHeight = this.Height;
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            // Raise KeyUp base class event

            if (KeyUp != null)
            {
                KeyUp(this, e);
            }

            try
            {
                // Don´t process up/down/let/right/pageup/pagedown/home/end

                if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down) return;
                if (e.KeyCode == Keys.Left || e.KeyCode == Keys.Right) return;
                if (e.KeyCode == Keys.PageDown || e.KeyCode == Keys.PageUp) return;
                if (e.KeyCode == Keys.Home || e.KeyCode == Keys.End) return;

                // Remove *, ' e % from the string
                if (this.Text.EndsWith("*") || this.Text.EndsWith("'") || this.Text.EndsWith("%"))
                {
                    this.Text = this.Text.Substring(0, this.Text.Length - 1);
                    this.SelectionStart = this.Text.Length;
                    return;
                }

                if (this.Text.IndexOf("*") != -1 || this.Text.IndexOf("'") != -1 || this.Text.IndexOf("%") != -1)
                {
                    this.Text = this.Text.Replace("*", "").Replace("'", "").Replace("%", "");
                }

                // Enter raises Submit event
                if (e.KeyCode == Keys.Enter)
                {
                    this.Height = defaultHeight;
                    if (null != this.Submit)
                        Submit(this, e);
                    this.SelectionLength = 0;
                    return;
                }

                // Saves text and selection and apply a filter
                // in the DataView

                string wTexto = this.Text;
                int wSelecao = this.SelectionStart;
                int wTamanhoSelecao = this.SelectionLength;

                if (String.Empty != this.Text)
                    dataViewCombo.RowFilter = "substring(" + this.DisplayMember + ",1," + this.Text.Trim().Length + ") = '" + this.Text.Trim() + "'";
                else
                    dataViewCombo.RowFilter = String.Empty;

                this.BringToFront();
                this.Height = autoCompleteHeight;

                // Sets new text (auto-complete) and select the auto-complete
                // part of the text

                if (e.KeyCode != Keys.Back)
                {

                    if (dataViewCombo.Count == 0)
                        this.Text = wTexto;
                    this.SelectionStart = wSelecao;
                    this.SelectionLength = this.Text.Length;
                }
                else
                {
                    this.SelectedIndex = -1;
                    this.Text = wTexto;
                    this.SelectionStart = wSelecao;
                    this.SelectionLength = wTamanhoSelecao;
                }

            }
            // Verify errors
            catch (Exception ex)
            {
                lastErrorDescription = ex.Message;
                stackTraceError = ex.StackTrace;
                ShowError();
            }
        }

        protected override void OnLeave(EventArgs e)
        {
            base.OnLeave(e);

            // Sets original Combo height when it looses focus
            if (Leave != null)
            {
                Leave(this, e);
            }

            if(null != this.Submit)
                this.Submit(this, new KeyEventArgs(Keys.Enter));
            this.Height = defaultHeight;
        }

        private void ShowError()
        {
            // Show error on screen

            MessageBoxEx.ShowAlert("Unexpected error: " + Environment.NewLine + lastErrorDescription +
                Environment.NewLine + stackTraceError + Environment.NewLine + " O erro foi salvo em um arquivo de log e o programa tentará continuar.");

        }

        #endregion

    }
}
